# -*- coding: utf-8 -*-

"""
.. todo:: docstring
.. todo:: ninepatch
.. todo:: surfaceobject



"""



__all__ = ["SubPixelSurfacePygame"]







# -----------------------------------------------------------------------------
# TODO: ninepatchsurface
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
import pygame
from math import floor


class SubPixelSurfacePygame(object):
    """
    This is a SubPixelSurface using pygame only methods.

    It is inspired by:

    http://www.pygame.org/project-Sub-pixel+surface-413-.html

    Thanks to Will for the original code using numeric.

    :Usage:

        x, y = 1.23, 2.345 # floats
        pingball = pygame.image.load("ball.png")
        pingball_subpixel = SubPixelSurface(pingball, level)
        screen.blit(pingball_subpixel.at(x, y), (x, y))

    :Author: DR0ID
    :Date: April 2011
    :License: Public Domain

    """

    def __init__(self, surface, x_level=3, y_level=None):

        """
        Creates a sub pixel surface object.

        If the image has artefacts at the sides,
        try to make a transparent, 1 pixel wide border around the image.

        :Parameters:
            surface : Surface
                a pygame surface to apply subpixeling
            x_level : int
                number of sub-pixel levels in x direction
                defautl: 3, but range is unlimited: 1 - inf, but be aware it will use level*level images taking memory
            y_level : int
                number of sub-pixel levels in y direction
                defautl: 3, but range is unlimited: 1 - inf, but be aware it will use level*level images taking memory
        """

        self.x_level = x_level
        self.y_level = y_level or x_level

        steps_x = [float(n) / self.x_level for n in xrange(self.x_level)]
        steps_y = [float(n) / self.y_level for n in xrange(self.y_level)]

        self.surfaces = []

        for frac_y in steps_y:
            row = []
            self.surfaces.append(row)
            for frac_x in steps_x:
                row.append( SubPixelSurfacePygame.generate(surface, frac_x, frac_y, x_level, self.y_level))

    @staticmethod
    def generate(surface, frac_x, frac_y, x_level=3, y_level=3):
        """
        Generates a subpixel image.

        It might be fast enough to be used directly for rendering, but
        be aware that is will temporary use about
        level * level * surface_size * surface_size memory.

        :Parameters:
            surface : Surface
                a pygame surface to apply subpixeling
            frac_x : float
                the fraction of the pixel, frac_x < 1.0
            frac_y : float
                the fraction of the pixel, frac_y < 1.0
            x_level : int
                number of sub-pixel levels in x direction
                defautl: 3, but range is unlimited: 1 - inf
            y_level : int
                number of sub-pixel levels in y direction
                default: 3, but range is unlimited: 1 - inf

        :returns: surface, shifted for subpixels according to frac_x and frac_y

        """

        # get the number of pixels to move from the fractions
        surf_x = int( frac_x * x_level )
        surf_y = int( frac_y * y_level )

        level = max(x_level, y_level)

        assert surf_x < level and surf_y < level
        assert x_level > 0
        assert y_level > 0

        print("level:" + str(x_level) + ", " + str(y_level) + " surf x,y: " + str(surf_x) + ", " + str(surf_y))

        # inflate image so each pixel is spread over 'number of level' pixels
        orig_w, orig_h = surface.get_size()
        w = x_level * orig_w
        h = y_level * orig_h
        s = pygame.transform.smoothscale(surface, (w, h))

        # make new surface with space for the shift in pixels
        surf = pygame.Surface((w + x_level, h + y_level), pygame.SRCALPHA)
        surf.fill((0,0,0,0))

        # blit the scaled up original at the shifted position
        surf.blit(s, (surf_x, surf_y), None, pygame.BLEND_RGBA_ADD)

        # scale back to original size + 1 (one additional pixel for sub-pixel)
        # here the smoothscale is appropriate
        surf = pygame.transform.smoothscale(surf, (orig_w + 1, orig_h + 1))

        return surf


    def at(self, x, y):

        """
        Gets a sub-pixel surface for a given coordinate.

        :Parameters:
            x : float
                X coordinate
            y : float
                Y coordinate

        :returns: surface corresponding to the fractions
        """

        surf_x = int( (x - floor(x)) * self.x_level )
        surf_y = int( (y - floor(y)) * self.y_level )

        return self.surfaces[surf_y][surf_x]

# -----------------------------------------------------------------------------

# TODO: SurfaceObject

# #!/usr/bin/python
# # -*- coding: utf-8 -*-
# u"""
# TODO: doc
# """

# __version__ = '$Id: surfaceobject.py 378 2010-05-15 05:21:24Z dr0iddr0id@gmail.com $'

# import pygame
# try:
    # import pygame.gfxdraw
# except:
    # pass
# import new

# # save a pointer to the original pygame.Surface
# pygame_surface = pygame.Surface

# class SurfaceObject(pygame_surface):
    # u"""
    # SurfaceObject is a Surface that has all all functions from pygame.draw, pygame.gfxdraw,
    # pygame.transform as a own method. So instead of calling

        # s = pygame.Surface((10,10))
        # pygame.draw.line(...)

    # with this SurfaceObject one can do:

        # s = SurfaceObject((10,10))
        # s.draw_line(...)

    # The SurfaceObject might be slower than using the native functions.
    # At the end, the SurfaceObject needs to be blitted to the screen.
    # """

    # def __init__(self, size, flags=0, depth=32):
        # super(SurfaceObject, self).__init__(size, flags, depth)

        # # add methods from pygame.draw
        # for name in dir(pygame.draw):
            # if not name.startswith('__'):
                # #new.instancemethod(function, instance, class)
                # attr = getattr(pygame.draw, name)
                # setattr(self, 'draw_'+name, new.instancemethod(attr, self, self.__class__))
        # try:
            # # add methods from gfxdraw, new in pygame 1.9
            # for name in dir(pygame.gfxdraw):
                # if not name.startswith('__'):
                    # setattr(self, "gfxdraw_"+name, new.instancemethod(getattr(pygame.gfxdraw, name), self, self.__class__))
        # except:
            # pass

        # # wrapper for functions that return a pygame.Surface
        # # wrapper returns a SurfaceObject instaed of a pygame.Surface
        # class Wrapper(object):
            # def __init__(self, func):
                # self._func = func
            # def __call__(self, *args, **kwargs):
                # s = self._func(*args, **kwargs)
                # # not sure if this creates a surface that is the same as converted surface
                # so = SurfaceObject(s.get_size(), 0, s)
                # # transfer the content
                # so.blit(s, (0,0), )
                # return so

        # for name in dir(pygame.transform):
            # if not name.startswith('__'):
                # if name in ['threshold', 'average_color']:
                    # # this functions take an surf, return a non surf
                    # setattr(self, name, getattr(pygame.transform, name))
                # elif name in ['set_smoothscale_backend', 'get_smoothscale_backend']:
                    # # this functions take not a surface and return not a surface
                    # # this wrapper is there to just filter out the self argument of the methods
                    # class Wrapper1(object):
                        # def __init__(self, func):
                            # self._func = func
                        # def __call__(self, *args, **kwargs):
                            # return self._func(*args, **kwargs)
                    # w = Wrapper1(getattr(pygame.transform, name))
                    # inst = new.instancemethod(w, self, self.__class__)
                    # setattr(self, name, inst)
                # else:
                    # # return a surface
                    # w = Wrapper(getattr(pygame.transform, name))
                    # inst = new.instancemethod(w, self, self.__class__)
                    # setattr(self, name, inst)

        # # some methods from pygame.Surface return a Surface, they need to
        # # be wrapped to return a SurfaceObject
        # for name in ['convert', 'convert_alpha', 'subsurface']:
            # orig_name = '_orig_'+name
            # orig_func = getattr(self, name)
            # setattr(self, name, Wrapper(orig_func))
            # setattr(self, orig_name, orig_func)

# # # only allow SurfaceObject to be instanciated, even using pygame.Surface
# # pygame.Surface = SurfaceObject
# # pygame.surface.Surface= SurfaceObject

# if __name__ == '__main__':
    # # usage
    # pygame.init()

    # s = SurfaceObject((400,300))
    # s.fill((255,0,0))
    # s.draw_line((255,255,0), (100,100), (400,300), 1)
    # s.draw_circle((0,0,255), (200,200), 50)
    # s.draw_line((255,255,255), (100,100), (300,300), 5)
    # #s.set_colorkey((255,0,0))
    # #s = s.rotate(90)
    # s2 = pygame.Surface((50,50))
    # s2.fill((255,255,255))
    # s2.draw_line((100,100,100), (0,0), (25,25), 3)

    # # print all attributes from a SurfaceObject
    # for i in dir(s): print i, '\t\t\t', getattr(s, i)

    # screen = pygame.display.set_mode((800, 600))
    # s2 = s2.convert()

    # # update display
    # screen.blit(s, (0,0))
    # screen.blit(s2, (400,0))
    # pygame.display.flip()

    # # wait for an event
    # pygame.event.clear()
    # pygame.event.wait()


# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# -----------------------------------------------------------------------------
